# _*_ coding: UTF-8 _*_
# @Time     : 2021/2/25 上午 10:32
# @Author   : Li Jie
# @Site     : http://www.hzdledu.com/
# @File     : prac2_server.py
# @Software : PyCharm

# 以多线程的方式实现一个简易聊天室
'''
Chat Server（服务器）
接收客户端的连接请求
从客户端读入消息并发布到各个客户端

Chat client(客户端）
监听服务器发来的消息
用户可以发送信息到服务器
'''
from threading import Thread
from queue import Queue
import socket
import select


class Server():
    def __init__(self, host, port):
        self.host = host  # 服务器IP
        self.port = port  # 服务器端口
        self.sockets = []  # 保存所有套接字的列表，包括服务器的套接字
        self.names = {}  # 保存所有客户端的套接字:名字的字典
        self.data_queue = Queue()  # 服务器发送和接收数据线程通信的队列
        self.start_server()  # 启动服务器
        self.start_threads()  # 启动服务的发送和接收线程

    # 启动服务器
    def start_server(self):
        print('初始化服务器端...')
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 1.创建socket对象
        self.server_socket.bind((self.host, self.port))  # 2.绑定ip和port
        self.server_socket.listen(5)  # 3.开始监听，设置最大连接数为5
        self.sockets.append(self.server_socket)
        print('服务器端等待客户端连接...')

    # 启动服务器的发送和接收线程
    def start_threads(self):
        print('初始化服务器端接收和发送线程...')
        self.thread_recv = Thread(target=self.recv_data, name='server_recv_thread')
        self.thread_send = Thread(target=self.send_data, name='server_send_thread')
        self.thread_recv.start()
        self.thread_send.start()
        self.thread_recv.join()
        self.thread_send.join()

    # 接收数据，放入队列
    def recv_data(self):
        while True:
            # 开始select 监听, 对sockets 中的服务器端server 进行监听
            # 一旦调用socket的send, recv函数，将会再次调用此模块
            rs, ws, es = select.select(self.sockets, [], [])
            # 循环判断是否有客户端连接进来, 当有客户端连接进来时select 将触发
            for temp in rs:
                if temp is self.server_socket:
                    self.add_client()
                else:
                    try:
                        data = f'{self.names[temp]}说:{temp.recv(1024).decode()}'
                        print(data)
                        self.data_queue.put(data)
                    except socket.error:
                        data = f'{self.names[temp]}离开'
                        self.data_queue.put(data)
                        self.sockets.remove(temp)

    # 送队列中取出数据，发送给每一个客户端
    def send_data(self):
        while True:
            while not self.data_queue.empty():
                data = self.data_queue.get(True, 2)
                for client in self.sockets:
                    if client != self.server_socket:
                        try:
                            client.send(data.encode())
                        except Exception as e:
                            print(e)

    # 增加客户端
    def add_client(self):
        client_socket, addr = self.server_socket.accept()
        try:
            client_socket.send('请输入名字'.encode())
            name = client_socket.recv(1024).decode()
            self.sockets.append(client_socket)
            self.names[client_socket] = name
            data = f'欢迎{name}进入聊天室:{addr}'
            print(data)
            self.data_queue.put(data)

            names = [self.names[k] for k in self.names]
            data = f'当前聊天室用户有:{names}'
            self.data_queue.put(data)
        except Exception as e:
            print(e)


if __name__ == '__main__':
    server_thread = Server('127.0.0.1', 8888)
